{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\nratings.csv 是评分\\ntags.csv 是电影的标签\\n'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import zipfile\n",
    "zFile = zipfile.ZipFile(\"ml-latest-small.zip\", \"r\")\n",
    "#ZipFile.namelist(): 获取ZIP文档内所有文件的名称列表\n",
    "for fileM in zFile.namelist(): \n",
    "    zFile.extract(fileM)\n",
    "\"\"\"\n",
    "ratings.csv 是评分\n",
    "tags.csv 是电影的标签\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "all_ratings = pd.read_csv(\"ml-latest-small/ratings.csv\")\n",
    "all_ratings[\"timestamp\"] = pd.to_datetime(all_ratings['timestamp'],unit='s')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<script>\n",
       "    require.config({\n",
       "        paths: {\n",
       "            'echarts':'https://assets.pyecharts.org/assets/echarts.min'\n",
       "        }\n",
       "    });\n",
       "</script>\n",
       "\n",
       "        <div id=\"33cc4299ce04446499153123992e2c6e\" style=\"width:900px; height:500px;\"></div>\n",
       "\n",
       "<script>\n",
       "        require(['echarts'], function(echarts) {\n",
       "                var chart_33cc4299ce04446499153123992e2c6e = echarts.init(\n",
       "                    document.getElementById('33cc4299ce04446499153123992e2c6e'), 'white', {renderer: 'canvas'});\n",
       "                var option_33cc4299ce04446499153123992e2c6e = {\n",
       "    \"animation\": true,\n",
       "    \"animationThreshold\": 2000,\n",
       "    \"animationDuration\": 1000,\n",
       "    \"animationEasing\": \"cubicOut\",\n",
       "    \"animationDelay\": 0,\n",
       "    \"animationDurationUpdate\": 300,\n",
       "    \"animationEasingUpdate\": \"cubicOut\",\n",
       "    \"animationDelayUpdate\": 0,\n",
       "    \"color\": [\n",
       "        \"#c23531\",\n",
       "        \"#2f4554\",\n",
       "        \"#61a0a8\",\n",
       "        \"#d48265\",\n",
       "        \"#749f83\",\n",
       "        \"#ca8622\",\n",
       "        \"#bda29a\",\n",
       "        \"#6e7074\",\n",
       "        \"#546570\",\n",
       "        \"#c4ccd3\",\n",
       "        \"#f05b72\",\n",
       "        \"#ef5b9c\",\n",
       "        \"#f47920\",\n",
       "        \"#905a3d\",\n",
       "        \"#fab27b\",\n",
       "        \"#2a5caa\",\n",
       "        \"#444693\",\n",
       "        \"#726930\",\n",
       "        \"#b2d235\",\n",
       "        \"#6d8346\",\n",
       "        \"#ac6767\",\n",
       "        \"#1d953f\",\n",
       "        \"#6950a1\",\n",
       "        \"#918597\"\n",
       "    ],\n",
       "    \"series\": [\n",
       "        {\n",
       "            \"type\": \"bar\",\n",
       "            \"name\": \"rating\",\n",
       "            \"data\": [\n",
       "                26818,\n",
       "                20047,\n",
       "                13211,\n",
       "                13136,\n",
       "                8551,\n",
       "                7551,\n",
       "                5550,\n",
       "                2811,\n",
       "                1791,\n",
       "                1370\n",
       "            ],\n",
       "            \"barCategoryGap\": \"20%\",\n",
       "            \"label\": {\n",
       "                \"show\": true,\n",
       "                \"position\": \"top\",\n",
       "                \"margin\": 8\n",
       "            }\n",
       "        }\n",
       "    ],\n",
       "    \"legend\": [\n",
       "        {\n",
       "            \"data\": [\n",
       "                \"rating\"\n",
       "            ],\n",
       "            \"selected\": {\n",
       "                \"rating\": true\n",
       "            },\n",
       "            \"show\": true,\n",
       "            \"padding\": 5,\n",
       "            \"itemGap\": 10,\n",
       "            \"itemWidth\": 25,\n",
       "            \"itemHeight\": 14\n",
       "        }\n",
       "    ],\n",
       "    \"tooltip\": {\n",
       "        \"show\": true,\n",
       "        \"trigger\": \"item\",\n",
       "        \"triggerOn\": \"mousemove|click\",\n",
       "        \"axisPointer\": {\n",
       "            \"type\": \"line\"\n",
       "        },\n",
       "        \"textStyle\": {\n",
       "            \"fontSize\": 14\n",
       "        },\n",
       "        \"borderWidth\": 0\n",
       "    },\n",
       "    \"xAxis\": [\n",
       "        {\n",
       "            \"show\": true,\n",
       "            \"scale\": false,\n",
       "            \"nameLocation\": \"end\",\n",
       "            \"nameGap\": 15,\n",
       "            \"gridIndex\": 0,\n",
       "            \"inverse\": false,\n",
       "            \"offset\": 0,\n",
       "            \"splitNumber\": 5,\n",
       "            \"minInterval\": 0,\n",
       "            \"splitLine\": {\n",
       "                \"show\": false,\n",
       "                \"lineStyle\": {\n",
       "                    \"show\": true,\n",
       "                    \"width\": 1,\n",
       "                    \"opacity\": 1,\n",
       "                    \"curveness\": 0,\n",
       "                    \"type\": \"solid\"\n",
       "                }\n",
       "            },\n",
       "            \"data\": [\n",
       "                4.0,\n",
       "                3.0,\n",
       "                5.0,\n",
       "                3.5,\n",
       "                4.5,\n",
       "                2.0,\n",
       "                2.5,\n",
       "                1.0,\n",
       "                1.5,\n",
       "                0.5\n",
       "            ]\n",
       "        }\n",
       "    ],\n",
       "    \"yAxis\": [\n",
       "        {\n",
       "            \"show\": true,\n",
       "            \"scale\": false,\n",
       "            \"nameLocation\": \"end\",\n",
       "            \"nameGap\": 15,\n",
       "            \"gridIndex\": 0,\n",
       "            \"inverse\": false,\n",
       "            \"offset\": 0,\n",
       "            \"splitNumber\": 5,\n",
       "            \"minInterval\": 0,\n",
       "            \"splitLine\": {\n",
       "                \"show\": false,\n",
       "                \"lineStyle\": {\n",
       "                    \"show\": true,\n",
       "                    \"width\": 1,\n",
       "                    \"opacity\": 1,\n",
       "                    \"curveness\": 0,\n",
       "                    \"type\": \"solid\"\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    ],\n",
       "    \"title\": [\n",
       "        {\n",
       "            \"text\": \"\\u67f1\\u5f62\\u56fe\",\n",
       "            \"padding\": 5,\n",
       "            \"itemGap\": 10\n",
       "        }\n",
       "    ]\n",
       "};\n",
       "                chart_33cc4299ce04446499153123992e2c6e.setOption(option_33cc4299ce04446499153123992e2c6e);\n",
       "        });\n",
       "    </script>\n"
      ],
      "text/plain": [
       "<pyecharts.render.display.HTML at 0x7f43d4c70280>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from eplot import eplot\n",
    "df = all_ratings[\"rating\"].value_counts()\n",
    "df.eplot.bar(title='柱形图')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 评分大于3分表示喜欢这个电影\n",
    "all_ratings[\"like\"] = all_ratings[\"rating\"]>=4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_num = 200\n",
    "# 训练数据\n",
    "train_ratings = all_ratings[all_ratings['userId'].isin(range(train_num))]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "like_ratings = train_ratings[train_ratings[\"like\"] == True]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 每一个人喜欢哪一些电影\n",
    "like_by_user = dict((k,frozenset(v.values)) for k,v in like_ratings.groupby(\"userId\")[\"movieId\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 电影被人喜欢的数量\n",
    "num_like_of_movie = like_ratings[[\"movieId\",\"like\"]].groupby(\"movieId\").sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "frequent_itemsets = {}\n",
    "min_support = 50\n",
    "# first step 步骤一：生成初始的频繁数据集\n",
    "frequent_itemsets[1] = dict((frozenset((movie_id,)),row[\"like\"])\n",
    "                            for movie_id,row in num_like_of_movie.iterrows()\n",
    "                            if row[\"like\"] > min_support)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 步骤②③，\n",
    "from collections import defaultdict\n",
    "def find_new_frequent_items(movies_like_by_user,frequent_of_k,min_support):\n",
    "    \"\"\"\n",
    "        movies_like_by_user:用户喜欢电影的集合\n",
    "        frequent_of_k：超集\n",
    "        min_support:最小的支持度\n",
    "    \"\"\"\n",
    "    counts = defaultdict(int)\n",
    "    # 获得用户看movie的集合\n",
    "    for user,movie_ids in movies_like_by_user.items(): \n",
    "        # 遍历超集中间的数据项\n",
    "        for itemset in frequent_of_k:\n",
    "            # 如数据项在用户的movie集合中，则代表用户同时喜欢这几部电影\n",
    "            if itemset.issubset(movie_ids):\n",
    "                # 遍历出现在movie集合但是没有出现在数据项中间的数据\n",
    "                for other_movie in movie_ids - itemset:\n",
    "                    current_superset = itemset | frozenset((other_movie,))\n",
    "                    counts[current_superset] += 1\n",
    "    return dict([(itemset,support) for itemset,support in counts.items()\n",
    "                            if support >= min_support])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "准备进行2项生成备选项集\n",
      "准备进行3项生成备选项集\n"
     ]
    }
   ],
   "source": [
    "for k in range(2,10):\n",
    "    current_set = find_new_frequent_items(like_by_user,frequent_itemsets[k-1],min_support)\n",
    "    if len(current_set) ==0:\n",
    "        print(\"{}项生成的备选项集长度为0，不再进行生成\".format(k))\n",
    "        break\n",
    "    else:\n",
    "        print(\"准备进行{}项生成备选项集\".format(k))\n",
    "        frequent_itemsets[k] = current_set\n",
    "        \n",
    "del frequent_itemsets[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 生成规则\n",
    "rules = []\n",
    "for k,item_counts in frequent_itemsets.items():\n",
    "    # k代表项数，item_counts代表里面的项\n",
    "    for item_set in item_counts.keys():\n",
    "        for item in item_set:\n",
    "            premise = item_set - set((item,))\n",
    "            rules.append((premise,item))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 得到每一条规则在训练集中的应验的次数\n",
    "# 应验\n",
    "right_rule = defaultdict(int)\n",
    "# 没有应验\n",
    "out_rule = defaultdict(int)\n",
    "\n",
    "for user,movies in like_by_user.items():\n",
    "    for rule in rules:\n",
    "        # premise,item代表购买了premise就会购买item\n",
    "        premise,item = rule\n",
    "        if premise.issubset(movies):\n",
    "            if item in movies:\n",
    "                right_rule[rule] +=1\n",
    "            else:\n",
    "                out_rule[rule] += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算每一条规则的置信度\n",
    "rule_confidence = {rule:right_rule[rule]/float(right_rule[rule] + out_rule[rule]) for rule in rules}\n",
    "from operator import itemgetter\n",
    "# 进行从大到小排序\n",
    "sort_confidence = sorted(rule_confidence.items(),key=itemgetter(1),reverse = True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算X在训练集中出现的次数\n",
    "item_num = defaultdict(int)\n",
    "for user,movies in like_by_user.items():\n",
    "    for rule in rules:\n",
    "        premise,item = rule\n",
    "        if item in movies:\n",
    "            item_num[rule] += 1\n",
    "            \n",
    "# 计算P(X) item_num[rule]代表的就是P(X)\n",
    "item_num = {k: v/len(like_by_user) for k, v in item_num.items()}   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算每一条规则的Lift\n",
    "rule_lift = {rule:(right_rule[rule]/(float(right_rule[rule] + out_rule[rule])))/item_num[rule] for rule in rules}\n",
    "from operator import itemgetter\n",
    "sort_lift = sorted(rule_lift.items(),key=itemgetter(1),reverse = True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 去除训练使用的数据集得到测试集\n",
    "ratings_test  = all_ratings.drop(train_ratings.index)\n",
    "# 去除测试集中unlike数据\n",
    "like_ratings_test = ratings_test[ratings_test[\"like\"]]\n",
    "user_like_test = dict((k,frozenset(v.values)) for k,v in like_ratings_test.groupby(\"userId\")[\"movieId\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sort_lift验证的正确度为：0.746268656716418\n",
      "sort_lift验证的正确度为：0.725\n",
      "sort_lift验证的正确度为：0.8433734939759037\n",
      "sort_lift验证的正确度为：0.8529411764705882\n",
      "sort_lift验证的正确度为：0.8524590163934426\n",
      "sort_lift验证的正确度为：0.8481012658227848\n",
      "sort_lift验证的正确度为：0.7215189873417721\n",
      "sort_lift验证的正确度为：0.7761194029850746\n",
      "sort_lift验证的正确度为：0.6712328767123288\n",
      "sort_lift验证的正确度为：0.8157894736842105\n",
      "sort_lift验证的正确度为：0.8857142857142857\n",
      "sort_lift验证的正确度为：0.9210526315789473\n",
      "sort_lift验证的正确度为：0.6351351351351351\n",
      "sort_lift验证的正确度为：0.9298245614035088\n",
      "sort_lift验证的正确度为：0.8947368421052632\n",
      "sort_lift验证的正确度为：0.8375\n",
      "sort_lift验证的正确度为：0.6973684210526315\n",
      "sort_lift验证的正确度为：0.7526881720430108\n",
      "sort_lift验证的正确度为：0.7285714285714285\n",
      "sort_lift验证的正确度为：0.49333333333333335\n"
     ]
    }
   ],
   "source": [
    "for i in range(0,20):\n",
    "    # 应验\n",
    "    right_rule = 0\n",
    "    # 没有应验\n",
    "    out_rule = 0\n",
    "    for movies in user_like_test.values():\n",
    "        if(sort_lift[i][0][0].issubset(movies)):\n",
    "            if(sort_lift[i][0][1] in movies):\n",
    "                right_rule +=1\n",
    "            else:\n",
    "                out_rule +=1\n",
    "    print(\"sort_lift验证的正确度为：{}\".format(right_rule/(right_rule+out_rule)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sort_confidence验证的正确度为：0.8524590163934426\n",
      "sort_confidence验证的正确度为：0.8857142857142857\n",
      "sort_confidence验证的正确度为：0.9210526315789473\n",
      "sort_confidence验证的正确度为：0.9298245614035088\n",
      "sort_confidence验证的正确度为：0.8375\n",
      "sort_confidence验证的正确度为：0.6271186440677966\n",
      "sort_confidence验证的正确度为：0.6065573770491803\n",
      "sort_confidence验证的正确度为：0.8382352941176471\n",
      "sort_confidence验证的正确度为：0.8454545454545455\n",
      "sort_confidence验证的正确度为：0.7422680412371134\n",
      "sort_confidence验证的正确度为：0.8433734939759037\n",
      "sort_confidence验证的正确度为：0.8813559322033898\n",
      "sort_confidence验证的正确度为：0.8529411764705882\n",
      "sort_confidence验证的正确度为：0.819672131147541\n",
      "sort_confidence验证的正确度为：0.6545454545454545\n",
      "sort_confidence验证的正确度为：0.8481012658227848\n",
      "sort_confidence验证的正确度为：0.746268656716418\n",
      "sort_confidence验证的正确度为：0.8545454545454545\n",
      "sort_confidence验证的正确度为：0.8596491228070176\n",
      "sort_confidence验证的正确度为：0.7761194029850746\n"
     ]
    }
   ],
   "source": [
    "for j in range(0,20):\n",
    "    # 应验\n",
    "    right_rule = 0\n",
    "    # 没有应验\n",
    "    out_rule = 0\n",
    "    for movies in user_like_test.values():\n",
    "        if(sort_confidence[j][0][0].issubset(movies)):\n",
    "            if(sort_confidence[j][0][1] in movies):\n",
    "                right_rule +=1\n",
    "            else:\n",
    "                out_rule +=1\n",
    "    print(\"sort_confidence验证的正确度为：{}\".format(right_rule/(right_rule+out_rule)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
